<?php

/**
 * @desc		框架入口文件
 * ---------------------------------------------------------------------
 * @author	unphp <unphp@qq.com>
 * @date		2014-03-05
 * @copyright	UnPHP 1.1.1
 * ---------------------------------------------------------------------
 */

use UnPHP\Core\ReadConf;
use UnPHP\Core\LoadNamespace;
use UnPHP\Core\LoadDefault;
use UnPHP\Core\Dispatcher;
use UnPHP\Core\RequestHttp;
use UnPHP\Core\ExceptionHandler;
use UnPHP\Core\BootstrapAbstract;

class App
{

        private static $_app            = null;
        private static $_global_library = array();
        private static $_app_dispatcher = null;
        private $_exception_handler     = array();
        private $_config                = null;
        private $_ext                   = array();
        private $ds                     = null;

        /**
         *
         * @var UnPHP\Core\Dispatcher 
         */
        private $_dispatcher = null;     // 应用调度分配器
        private $_modules    = array();     // 应用“模块”列表---来之于配置app.modules

        /**
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         * @param type $conf
         * @return \UnPHP
         */

        public function __construct($conf)
        {
                define('CORE_PATH', dirname(__FILE__));
                $this->ds      = DIRECTORY_SEPARATOR;
                $this->iniLoadFile();
                $ReadConf      = new ReadConf($conf);
                $this->_config = $ReadConf->get();
                // 注册“自动加载”接管函数
                $this->registerAutoLoad();
                $this->init();
                require CORE_PATH . '/Lib/common.function.php';
                return $this;
        }

        /**
         * 初始化框架
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         */
        private function init()
        {
                error_reporting(E_ALL);
                $this->_dispatcher = self::dispatcher();
                self::$_app        = $this;
                //框架默认的异常接管
                $this->_exception_handler[] = function ($e) {
                        ExceptionHandler::defaultException();
                };
        }

        /**
         * 应用设置自己的异常接管
         * @param type $f
         * @param type $elvel
         */
        public function setExceptionHandler($f, $elvel = 0)
        {
                switch ($elvel)
                {
                        case 0:
                                $this->_exception_handler[] = $f;
                                break;
                        case 1:
                                array_shift($this->_exception_handler);
                                $this->_exception_handler[] = $f;
                                break;
                        default:
                                $this->_exception_handler   = array();
                                $this->_exception_handler[] = $f;
                                break;
                }
        }

        /**
         * 载入框架核心类文件
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         */
        private function iniLoadFile()
        {
                include __DIR__ . $this->ds . 'Core' . $this->ds . 'ReadConf.class.php';
                include __DIR__ . $this->ds . 'Core' . $this->ds . 'Dispatcher.class.php';
                include __DIR__ . $this->ds . 'Core' . $this->ds . 'RequestAbstract.class.php';
                include __DIR__ . $this->ds . 'Core' . $this->ds . 'RequestHttp.class.php';
                include __DIR__ . $this->ds . 'Core' . $this->ds . 'Router.class.php';
                include __DIR__ . $this->ds . 'Core' . $this->ds . 'RouteSimple.class.php';
                include __DIR__ . $this->ds . 'Core' . $this->ds . 'BootstrapAbstract.class.php';
        }

        /**
         * 注册“自动加载接管”函数
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         */
        private function registerAutoLoad()
        {
                include __DIR__ . $this->ds . 'Core' . $this->ds . 'LoadAbstract.class.php';
                include __DIR__ . $this->ds . 'Core' . $this->ds . 'LoadNamespace.class.php';
                include __DIR__ . $this->ds . 'Core' . $this->ds . 'LoadDefault.class.php';
                $load = isset($this->_config["app"]['load']) && in_array($this->_config["app"]['load'], array('default', 'namespace')) ? $this->_config["app"]['load'] : 'default';
                switch ($load)
                {
                        case 'namespace':

                                $auto = new LoadNamespace(__DIR__, $this->_config["app"]["library"], self::$_global_library);
                                break;

                        default:
                                $auto = new LoadDefault(__DIR__, $this->_config["app"]["library"], self::$_global_library);
                                break;
                }
                spl_autoload_register(array($auto, 'autoLoad'));
        }

        /**
         * 获取应用的配置
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         * @return array
         */
        public function getConfig()
        {
                return $this->_config;
        }

        /**
         * 获取应用配置中，声明的模块
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         * @return array
         */
        public function getModules()
        {
                return $this->_modules;
        }

        /**
         * 获取应用配置中，声明的模块
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         * @return array
         */
        public function createURL($m_c_a, $params = array(), $route_name = 'default')
        {
                return $this->_dispatcher->getRouter()->getRegisteredRoute($route_name)->createUrl($m_c_a, $params);
        }

        /**
         * 初始化应用
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         * @return UnPHP
         */
        public function bootstrap(BootstrapAbstract $bootstrap)
        {
                set_exception_handler(function($e) {
                        foreach ($this->_exception_handler as $f)
                        {
                                $f($e);
                        }
                        exit;
                });


                $request = new RequestHttp();
                $this->_dispatcher->setRequest($request);
                switch ($this->_config['app']['debug'])
                {
                        case 1:
                                error_reporting(E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_RECOVERABLE_ERROR);
                                break;
                        case 2 :
                                error_reporting(E_ALL);
                        default:
                                error_reporting(0);
                }
                $this->_modules = isset($this->_config['app']['modules']) ? explode(",", $this->_config['app']['modules']) : array();
                // 设置默认模块/控制器/方法
                isset($this->_config['app']['default_module']) ? $request->setDefaultModule($this->_config['app']['default_module']) : $request->setDefaultModule('index');
                isset($this->_config['app']['default_controller']) ? $request->setDefaultController($this->_config['app']['default_controller']) : $request->setDefaultController('index');
                isset($this->_config['app']['default_action']) ? $request->setDefaultAction($this->_config['app']['default_action']) : $request->setDefaultModule('index');
                //$this->extRegister();
                
                
                $this->checkException(!isset($this->_config['app']['root']), 'UnPHPExceptionStartupError', 'App\'s config params, "root" must set!');
                $magicMethods = array(
                    '__construct', '__destruct', '__call', '__callStatic',
                    '__get', '__set', '__isset', '__unset',
                    '__sleep', '__wakeup', '__toString', '__invoke',
                    '__set_state', '__clone'
                );
                $bootstrapRef = new ReflectionClass("Bootstrap");
                $methodList   = $bootstrapRef->getMethods();
                foreach ($methodList as $methodTemp)
                {
                        $f = substr($methodTemp->name, 0, 1);
                        if ('_' === $f && !in_array($methodTemp->name, $magicMethods))
                        {
                                $method = new ReflectionMethod($bootstrap, $methodTemp->name);
                                if (true === $method->isPublic())
                                {
                                        if ($method->getParameters())
                                        {
                                                $bootstrap->{$methodTemp->name}($this->_dispatcher);
                                        }
                                        else
                                        {
                                                $bootstrap->{$methodTemp->name}();
                                        }
                                }
                        }
                }

                return $this;
        }

        /**
         * 执行应用
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         */
        public function run($app_mode = "web")
        {
                $this->_dispatcher->setAppMode($app_mode);
                // 判断路由是否成功
                $this->_dispatcher->route();
                $module_name     = $this->_dispatcher->getRequest()->getModuleName();
                $controller_name = $this->_dispatcher->getRequest()->getControllerName();
                $action_name     = $this->_dispatcher->getRequest()->getActionName();
                // 判断模块开关
                $this->checkException(!in_array($module_name, $this->_modules), 'UnPHPExceptionLoadFailedModule', 'App\'s config params, "module" not record now for you request module("' . $module_name . '") !');
                $app_root        = $this->_config['app']['root'];
                $controller_dir  = isset($this->_config['app']['controller_dir']) ? $this->_config['app']['controller_dir'] : 'Controllers';
                $modules_dir     = isset($this->_config['app']['modules_dir']) ? $this->_config['app']['modules_dir'] : 'Modules';
                $controller_path = $controller_dir . DIRECTORY_SEPARATOR . ucfirst($controller_name) . 'Controller.class.php';
                $controller_file = 'index' === $module_name ?
                        $app_root . $this->ds . $controller_path :
                        $app_root . $this->ds . $modules_dir . $this->ds . ucfirst($module_name) . $this->ds . $controller_path;
                        $this->checkException(!file_exists($controller_file), 'UnPHPExceptionLoadFailed', $controller_file . ', Not found this file!');
                include $controller_file;
                $controller      = ucfirst($controller_name) . 'Controller';
                $action          = $action_name . 'Action';
                $controller_obj  = $this->checkClassMethod($controller, $action);
                if (is_object($controller_obj))
                {
                        $controller_obj->init();
                        $controller_obj->$action();
                }
        }

        /**
         * 检查，是否需要抛出异常。
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         * @param type $bool
         * @param type $exception_name
         * @param type $message
         * @throws type
         */
        public  function checkException($bool, $exception_name, $message)
        {
                if ($bool)
                {
                       $exception_name =  'UnPHP\Exception\\'.$exception_name;
                        throw  new $exception_name($message);
                }
        }

        /**
         * 检查“类/方法”是否存在，返回类实例对象。
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         * @param type $class
         * @param type $method
         * @return \class
         */
        private function checkClassMethod($class, $method)
        {
                try
                {
                        $ref_class = new ReflectionClass($class);
                }
                catch (Exception $exc)
                {
                        throw new UnPHPExceptionLoadFailedController($exc->getMessage() . ' Not found this action("' . $class . '")');
                }
                $this->checkException(!$ref_class->hasMethod($method), 'UnPHPExceptionLoadFailedAction', 'Not found this action("' . $method . '")!');
                $obj        = new $class($this->_dispatcher);
                $ref_method = new ReflectionMethod($obj, $method);
                $this->checkException(!$ref_method->isPublic(), 'UnPHPExceptionLoadFailedAction', 'This action("' . $method . '") not open permissions!');
                return $obj;
        }

        /**
         * 返回应用实例
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         * @return App
         */
        public static function app()
        {
                return self::$_app;
        }

        /**
         * 设置系统全局库
         * @author Xiao Tangren  <unphp@qq.com>
         * @data 2014-03-05
         * @param type $path
         */
        public static function setLibrary($path)
        {
                self::$_global_library[] = $path;
        }

        /**
         * 
         * @return  UnPHP\Core\Dispatcher 
         */
        public static function getLibrary()
        {
                return self::$_global_library;
        }

        /**
         * 
         * @return  UnPHP\Core\Dispatcher 
         */
        public static function dispatcher()
        {

                if (null === self::$_app_dispatcher)
                {
                        self::$_app_dispatcher = Dispatcher::getInstance();
                }
                return self::$_app_dispatcher;
        }

        /**
         * 
         * @return UnPHP_Dispatcher
         */
        public function getDispatcher()
        {
                return $this->_dispatcher;
        }

        public function error($msg)
        {
                $return           = array();
                $return['status'] = -1;
                $return['msg']    = $msg;
                $return['data']   = null;
                echo json_encode($return);
                exit;
        }

}
